#ifndef __PROCESS_EXEC_H__
#define __PROCESS_EXEC_H__

#include <process/process.h>

/**
 *  exec()
 *  spusti aplikaci <filename>
 */
int exec(char *filename) {
  unsigned int cluster;                                                         /* cluster */
  unsigned int t;
  unsigned int seg;

  char far *far_dirPointer         = EXEC_DIR_PTR;                              /* zde se bude zapisovat aktualni slozka, odkud se spoustela aplikace */
  char far *far_argumentsPointer   = EXEC_ARG_PTR;                              /* zde budou pripravene parametry pro aplikaci */
  char far *far_appName            = EXEC_APPNAME_PTR;                          /* zde bude nazev spustene aplikace */
  int i                            = 0;                                         /* cyklus */
  int arguments_count              = 0;                                         /* na jake pozici zacina string s parametry */
  DirectoryEntry *sfile            = malloc(sizeof(DirectoryEntry));            /* promenna pro hledani souboru */
  char *filenameLowercase          = malloc(strlen(filename)+1);                /* pokud je filename malymi pismeny, do teto promenne se prevedou na velka */
  char *acutalDirBackup            = malloc(256);                               /* zaloha aktualni slozky */
  char *arguments                  = malloc(strlen(filename)+1);                /* parametry pro aplikaci */

  /******     FUNKCE    ******/
  /****************************************************************************//**************************/
  /* zalohujeme si aktualni pracovni slozku */
  getcwd(acutalDirBackup);

  /* vyparsovani parametru pro aplikaci */
  if(pos(filename, ' ') != POS_NOT_FOUND) {                                     /* pokud je v nazvu souboru aspon jedna mezera */
    arguments_count = pos(filename, ' ')+1;                                     /* do promenne arguments_count se ulozi kde je mezera */
    while(*(filename + arguments_count) != '\0')                                /* cyklus zapise do promenne arguments parametry za mezerou */
      *(arguments + i++) = *(filename + arguments_count++);
    *(arguments + i) = '\0';                                                    /* uzavreme string */
    char2far(arguments, far_argumentsPointer);                                  /* pripravime string pro aplikaci */
    filename[arguments_count-i-1] = '\0';                                       /* ..aby nebyl parametr soucasti nazvu souboru */
    i=0;                                                                        /* .. <i> se bude jeste pouzivat */
  } else {                                                                      /* aplikace je pustena bez parametru */
    strcpy(arguments, "");
    char2far(arguments, far_argumentsPointer);
  }

  /* do <filenameLowercase> si zapiseme nazev souboru malymi pismeny */
  strtolower(filename, filenameLowercase);
  filename = filenameLowercase;                                                 /* prepiseme pointer */

  if(pos(filename, '/') != POS_NOT_FOUND) {                                     /* pokud je v souboru alespon jedno lomitko */
    filename[l_pos(filename, '/')] = '\0';                                      /* bude se jednat o cestu, za poslednim lomitkem zavreme string (zbyde nam jen string s nazvem souboru) */
    if(!strlen(filename))
      chdir("/");
    else
      chdir(filename);                                                          /* skocime do slozky */
    filename += strlen(filename)+1;                                             /* zmenime pointer jen na nazev souboru */
  }

  char2far(filename, far_appName);
  /* na adresu <far_appName> zapisu nazev souboru */
  /*strcpy(far_appName, filename);*/
  /* vyhledame soubor v aktualni slozce */
  while((i = findfile(sfile, i)) != END_OF_DIR) {
    /* soubor nalezen */
    if(!strcmp(sfile->realname, filename)) {
      /* zaznam je soubor */
      if(sfile->attr == F_ARCHIVE) {
        cluster = sfile->ClstrNo;
        goto print_out;
      }
    }
  }

  /* zmenime zpet pracovni slozku */
  chdir(acutalDirBackup);

  /* a uvolnime buffery */
  free(arguments);
  free(acutalDirBackup);
  free(filenameLowercase);
  free(sfile);
  return EXEC_FAILED;

  print_out:
    /* zjistime volny segment, kam bychom mohli aplikaci nahrat */
    seg = exec_first_free_segment();

    exec_set_segment_status(seg, true);

    /* predame na dannou adresu aktualni slozku, pro nas spousteny soubor */
    char2far(acutalDirBackup, far_dirPointer);

    /* rutina pro nahrani souboru do pameti */
    exec_load_app((unsigned int)seg, (unsigned int)cluster, (unsigned int)sfile->filesize);

    /* samotny proces spousteni v ASM */
    asm cli                           ;

    /* spusteni aplikace... > zalohujeme si stack pointer do registru BX */
    asm mov bx, sp                    ;

    /* nastavime segmentove registry */
    asm mov ax, seg                   ;
    asm mov ds, ax                    ;
    asm mov es, ax                    ;
    asm mov ss, ax                    ;

    /* nastavime stack pointer tesne pred konec segmentu */
    asm mov sp, 0xFFF0                ;

    /* do noveho stacku si ulozime dulezite registry */
    asm push bx                       ;
    asm push di                       ;
    asm push si                       ;
    asm sti                           ;

    /* skok do aplikace */
    asm cmp ax, 0x3000                ;
    asm jne E_4000                    ;
    exec_call_far(0x3000)             ;
    asm jmp E_EXIT                    ;

    E_4000:
    asm cmp ax, 0x4000                ;
    asm jne E_5000                    ;
    exec_call_far(0x4000)             ;
    asm jmp E_EXIT                    ;

    E_5000:
    asm cmp ax, 0x5000                ;
    asm jne E_6000                    ;
    exec_call_far(0x5000)             ;
    asm jmp E_EXIT                    ;

    E_6000:
    asm cmp ax, 0x6000                ;
    asm jne E_7000                    ;
    exec_call_far(0x6000)             ;
    asm jmp E_EXIT                    ;

    E_7000:
    asm cmp ax, 0x7000                ;
    asm jne E_8000                    ;
    exec_call_far(0x7000)             ;
    asm jmp E_EXIT                    ;

    E_8000:
    asm cmp ax, 0x8000                ;
    asm jne E_9000                    ;
    exec_call_far(0x8000)             ;
    asm jmp E_EXIT                    ;

    E_9000:
    asm cmp ax, 0x9000                ;
    asm jne E_A000                    ;
    exec_call_far(0x9000)             ;
    asm jmp E_EXIT                    ;

    E_A000:
    asm cmp ax, 0xA000                ;
    asm jne E_B000                    ;
    exec_call_far(0xA000)             ;
    asm jmp E_EXIT                    ;

    E_B000:
    asm cmp ax, 0xB000                ;
    asm jne E_C000                    ;
    exec_call_far(0xB000)             ;
    asm jmp E_EXIT                    ;

    E_C000:
    asm cmp ax, 0xC000                ;
    asm jne E_D000                    ;
    exec_call_far(0xC000)             ;
    asm jmp E_EXIT                    ;

    E_D000:
    asm cmp ax, 0xD000                ;
    asm jne E_EXIT                    ;
    exec_call_far(0xD000)             ;

    E_EXIT:
    asm cli                           ;

    /* zde se vratime, vratime zpet dulezite registry */
    asm pop si                        ;
    asm pop di                        ;
    asm pop bx                        ;

    /* nastavime zpet puvodni (kernel) segment [CS by se melo rovnat 0x1000] */
    asm mov ax, cs                    ;
    asm mov ds, ax                    ;
    asm mov es, ax                    ;
    asm mov ss, ax                    ;

    /* a vratime stack pointer */
    asm mov sp, bx                    ;
    asm sti                           ;

    /* segment se jiz nepouziva, tak ho uvolnime */
    exec_set_segment_status(seg, false);

    /* zmenime pracovni slozku zpet */
    chdir(acutalDirBackup);

    /* dealokujeme buffery */
    free(filenameLowercase);
    free(acutalDirBackup);
    free(arguments);
    free(sfile);


  return *EXEC_RETURN_VALUE;
}

#endif
